﻿using iTextSharp.text.pdf;
using iTextSharp.text.pdf.security;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.X509;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Una.FirmaDigital.LN.Dominio.Acciones
{
    public class Firma
    {
        public bool FirmarDesacopladoBasico(PdfSignatureAppearance sap, IExternalSignature externalSignature,
            ICollection<X509Certificate> chain, ICollection<ICrlClient> crlList,
            IOcspClient ocspClient, ITSAClient tsaClient,
            int estimatedSize, CryptoStandard sigtype, ref string mensaje)
        {

            try
            {
                List<X509Certificate> list = new List<X509Certificate>((IEnumerable<X509Certificate>)chain);
                ICollection<byte[]> crlBytes = (ICollection<byte[]>)null;
                int num = 0;
                while (crlBytes == null && num < list.Count)
                    crlBytes = MakeSignature.ProcessCrl(list[num++], crlList);
                if (estimatedSize == 0)
                {
                    estimatedSize = 8192;
                    if (crlBytes != null)
                    {
                        foreach (byte[] numArray in (IEnumerable<byte[]>)crlBytes)
                            estimatedSize += numArray.Length + 10;
                    }
                    if (ocspClient != null)
                        estimatedSize += 4192;
                    if (tsaClient != null)
                        estimatedSize += 4192;
                }
                sap.Certificate = list[0];
                if (sigtype == CryptoStandard.CADES)
                    sap.AddDeveloperExtension(PdfDeveloperExtension.ESIC_1_7_EXTENSIONLEVEL2);
                PdfSignature pdfSignature = new PdfSignature(PdfName.ADOBE_PPKLITE, sigtype == CryptoStandard.CADES ? PdfName.ETSI_CADES_DETACHED : PdfName.ADBE_PKCS7_DETACHED);
                pdfSignature.Reason = sap.Reason;
                pdfSignature.Location = sap.Location;
                pdfSignature.SignatureCreator = sap.SignatureCreator;
                pdfSignature.Contact = sap.Contact;
                pdfSignature.Date = new PdfDate(sap.SignDate);
                sap.CryptoDictionary = (PdfDictionary)pdfSignature;
                Dictionary<PdfName, int> exclusionSizes = new Dictionary<PdfName, int>();
                exclusionSizes[PdfName.CONTENTS] = estimatedSize * 2 + 2;
                sap.PreClose(exclusionSizes);
                string hashAlgorithm = externalSignature.GetHashAlgorithm();
                PdfPKCS7 pdfPkcS7 = new PdfPKCS7((ICipherParameters)null, chain, hashAlgorithm, false);
                DigestUtilities.GetDigest(hashAlgorithm);
                byte[] secondDigest = DigestAlgorithms.Digest(sap.GetRangeStream(), hashAlgorithm);
                DateTime now = DateTime.Now;
                byte[] ocsp = (byte[])null;
                if (chain.Count >= 2 && ocspClient != null)
                    ocsp = ocspClient.GetEncoded(list[0], list[1], (string)null);
                byte[] authenticatedAttributeBytes = pdfPkcS7.getAuthenticatedAttributeBytes(secondDigest, now, ocsp, crlBytes, sigtype);
                byte[] digest = externalSignature.Sign(authenticatedAttributeBytes);
                pdfPkcS7.SetExternalDigest(digest, (byte[])null, externalSignature.GetEncryptionAlgorithm());
                byte[] encodedPkcS7 = pdfPkcS7.GetEncodedPKCS7(secondDigest, now, tsaClient, ocsp, crlBytes, sigtype);
                if (estimatedSize < encodedPkcS7.Length)
                    throw new IOException("Not enough space");
                byte[] bytes = new byte[estimatedSize];
                Array.Copy((Array)encodedPkcS7, 0, (Array)bytes, 0, encodedPkcS7.Length);
                PdfDictionary update = new PdfDictionary();
                update.Put(PdfName.CONTENTS, (PdfObject)new PdfString(bytes).SetHexWriting(true));
                sap.Close(update);
            }
            catch (Exception ex)
            {
                mensaje = ex.Message + ex.StackTrace;
                throw ex;
            }

            return true;
        }

        public FileStream obtenerStreamDesdeSrcopen(string src, ref string mensaje)
        {
            try
            {
                FileStream archivo = new FileStream(src, FileMode.Open, FileAccess.Read);
                return archivo;
            }
            catch (Exception ex)
            {
                mensaje = ex.Message;
                throw ex;
            }
        }
        public FileStream obtenerStreamDesdeSrccreate(string src, ref string mensaje)
        {
            try
            {
                FileStream archivo = new FileStream(src, FileMode.Create);
                return archivo;
            }
            catch (Exception ex)
            {
                mensaje = ex.Message;
                throw ex;
            }
        }
        public Pkcs12Store obtenerPkcsStore(FileStream privatekeystream, string contrasena, ref string mensaje)
        {
            try
            {
                Pkcs12Store pk12 = new Pkcs12Store(privatekeystream, contrasena.ToCharArray());
                return pk12;
            }
            catch (Exception ex)
            {
                mensaje = ex.Message;
                throw ex;
            }

        }

        public PdfReader obtenerPdfdeSrc(string src, ref string mensaje)
        {
            try
            {
                PdfReader documento = new PdfReader(src);
                return documento;
            }
            catch (Exception ex)
            {
                mensaje = ex.Message;
                throw ex;
            }

        }

        public string obtenerNombreArchivo(string src, ref string mensaje)
        {
            try
            {
                return Path.GetFileName(src);

            }
            catch (Exception ex)
            {
                mensaje = ex.Message;
                throw ex;
            }
        }

        public string obtenerDetallesFirma(string archivopdf)
        {

            StringBuilder sb = new StringBuilder();
            PdfReader reader = new PdfReader(archivopdf);
            AcroFields af = reader.AcroFields;
            List<string> names = af.GetSignatureNames();
            for (int i = 0; i < names.Count; ++i)
            {
                String name = (string)names[i];
                PdfPKCS7 pk = af.VerifySignature(name);
                sb.AppendFormat("Nombre de región de firma: {0}\n", name);
                sb.AppendFormat("Nombre del firmante: {0}\n", pk.SignName);
                sb.AppendFormat("Fecha de la firma : {0}\n", pk.SignDate);
                sb.AppendFormat("Pais de origen: {0}\n", EN.PdfPKCS7.GetSubjectFields(pk.SigningCertificate).GetField("C"));
                sb.AppendFormat("Organización : {0}\n", EN.PdfPKCS7.GetSubjectFields(pk.SigningCertificate).GetField("O"));
                sb.AppendFormat("Unidad de Trabajo: {0}\n", EN.PdfPKCS7.GetSubjectFields(pk.SigningCertificate).GetField("OU"));
                sb.AppendFormat("Email del emisor: {0}\n", EN.PdfPKCS7.GetSubjectFields(pk.SigningCertificate).GetField("E"));
                sb.AppendFormat("Nombre Emisor: {0}\n", EN.PdfPKCS7.GetSubjectFields(pk.SigningCertificate).GetField("CN"));
            }

            return sb.ToString();
        }

        public string ObtenerDetalleEspecificoFirma(string archivopdf, string key)
        {
            StringBuilder sb = new StringBuilder();
            PdfReader reader = new PdfReader(archivopdf);
            AcroFields af = reader.AcroFields;
            List<string> names = af.GetSignatureNames();
            for (int i = 0; i < names.Count; ++i)
            {
                String name = (string)names[i];
                PdfPKCS7 pk = af.VerifySignature(name);
                sb.AppendFormat("Valor Solicitado: {0}\n",EN.PdfPKCS7.GetSubjectFields(pk.SigningCertificate).GetField(key));
            }
            return sb.ToString();
        }

    }
}
